﻿//tools functions，可单独使用,共用到两个全局变量wkh,$，其中$可以使用$.fullName()或wkh.select.fullName()取消

if (!wkh) {
    var wkh = {
        version: "1.01"
    };
}

/*!
 * Sizzle CSS Selector Engine v@VERSION
 * http://sizzlejs.com/
 *
 * Copyright 2013 jQuery Foundation, Inc. and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: @DATE
 */
wkh.tools = (function (window, undefined) {

    var i,
        support,
        cachedruns,
        Expr,
        getText,
        isXML,
        compile,
        outermostContext,
        sortInput,

        // Local document vars
        setDocument,
        document,
        docElem,
        documentIsHTML,
        rbuggyQSA,
        rbuggyMatches,
        matches,
        contains,

        // Instance-specific data
        expando = "sizzle" + -(new Date()),
        preferredDoc = window.document,
        dirruns = 0,
        done = 0,
        classCache = createCache(),
        tokenCache = createCache(),
        compilerCache = createCache(),
        hasDuplicate = false,
        sortOrder = function (a, b) {
            if (a === b) {
                hasDuplicate = true;
                return 0;
            }
            return 0;
        },

        // General-purpose constants
        strundefined = typeof undefined,
        MAX_NEGATIVE = 1 << 31,

        // Instance methods
        hasOwn = ({}).hasOwnProperty,
        arr = [],
        pop = arr.pop,
        push_native = arr.push,
        push = arr.push,
        slice = arr.slice,
        // Use a stripped-down indexOf if we can't use a native one
        indexOf = arr.indexOf || function (elem) {
            var i = 0,
                len = this.length;
            for (; i < len; i++) {
                if (this[i] === elem) {
                    return i;
                }
            }
            return -1;
        },

        booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",

        // Regular expressions

        // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
        whitespace = "[\\x20\\t\\r\\n\\f]",
        // http://www.w3.org/TR/css3-syntax/#characters
        characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",

        // Loosely modeled on CSS identifier characters
        // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
        // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
        identifier = characterEncoding.replace("w", "w#"),

        // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
        attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
            "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",

        // Prefer arguments quoted,
        //   then not containing pseudos/brackets,
        //   then attribute selectors/non-parenthetical expressions,
        //   then anything else
        // These preferences are here to reduce the number of selectors
        //   needing tokenize in the PSEUDO preFilter
        pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace(3, 8) + ")*)|.*)\\)|)",

        // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
        rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"),

        rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"),
        rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),

        rsibling = new RegExp(whitespace + "*[+~]"),
        rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g"),

        rpseudo = new RegExp(pseudos),
        ridentifier = new RegExp("^" + identifier + "$"),

        matchExpr = {
            "ID": new RegExp("^#(" + characterEncoding + ")"),
            "CLASS": new RegExp("^\\.(" + characterEncoding + ")"),
            "TAG": new RegExp("^(" + characterEncoding.replace("w", "w*") + ")"),
            "ATTR": new RegExp("^" + attributes),
            "PSEUDO": new RegExp("^" + pseudos),
            "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
                "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
                "*(\\d+)|))" + whitespace + "*\\)|)", "i"),
            "bool": new RegExp("^(?:" + booleans + ")$", "i"),
            // For use in libraries implementing .is()
            // We use this for POS matching in `select`
            "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
                whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i")
        },

        rnative = /^[^{]+\{\s*\[native \w/,

        // Easily-parseable/retrievable ID or TAG or CLASS selectors
        rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,

        rinputs = /^(?:input|select|textarea|button)$/i,
        rheader = /^h\d$/i,

        rescape = /'|\\/g,

        // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
        runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"),
        funescape = function (_, escaped, escapedWhitespace) {
            var high = "0x" + escaped - 0x10000;
            // NaN means non-codepoint
            // Support: Firefox
            // Workaround erroneous numeric interpretation of +"0x"
            return high !== high || escapedWhitespace ?
                escaped :
                // BMP codepoint
                high < 0 ?
                    String.fromCharCode(high + 0x10000) :
                    // Supplemental Plane codepoint (surrogate pair)
                    String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00);
        };

    // Optimize for push.apply( _, NodeList )
    try {
        push.apply(
            (arr = slice.call(preferredDoc.childNodes)),
            preferredDoc.childNodes
        );
        // Support: Android<4.0
        // Detect silently failing push.apply
        arr[preferredDoc.childNodes.length].nodeType;
    } catch (e) {
        push = {
            apply: arr.length ?

                // Leverage slice if possible
                function (target, els) {
                    push_native.apply(target, slice.call(els));
                } :

                // Support: IE<9
                // Otherwise append directly
                function (target, els) {
                    var j = target.length,
                        i = 0;
                    // Can't trust NodeList.length
                    while ((target[j++] = els[i++])) { }
                    target.length = j - 1;
                }
        };
    }

    function Sizzle(selector, context, results, seed) {
        var match, elem, m, nodeType,
            // QSA vars
            i, groups, old, nid, newContext, newSelector;

        if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
            setDocument(context);
        }

        context = context || document;
        results = results || [];

        if (!selector || typeof selector !== "string") {
            return results;
        }

        if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {
            return [];
        }

        if (documentIsHTML && !seed) {

            // Shortcuts
            if ((match = rquickExpr.exec(selector))) {
                // Speed-up: Sizzle("#ID")
                if ((m = match[1])) {
                    if (nodeType === 9) {
                        elem = context.getElementById(m);
                        // Check parentNode to catch when Blackberry 4.6 returns
                        // nodes that are no longer in the document #6963
                        if (elem && elem.parentNode) {
                            // Handle the case where IE, Opera, and Webkit return items
                            // by name instead of ID
                            if (elem.id === m) {
                                results.push(elem);
                                return results;
                            }
                        } else {
                            return results;
                        }
                    } else {
                        // Context is not a document
                        if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) &&
                            contains(context, elem) && elem.id === m) {
                            results.push(elem);
                            return results;
                        }
                    }

                    // Speed-up: Sizzle("TAG")
                } else if (match[2]) {
                    push.apply(results, context.getElementsByTagName(selector));
                    return results;

                    // Speed-up: Sizzle(".CLASS")
                } else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) {
                    push.apply(results, context.getElementsByClassName(m));
                    return results;
                }
            }

            // QSA path
            if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
                nid = old = expando;
                newContext = context;
                newSelector = nodeType === 9 && selector;

                // qSA works strangely on Element-rooted queries
                // We can work around this by specifying an extra ID on the root
                // and working up from there (Thanks to Andrew Dupont for the technique)
                // IE 8 doesn't work on object elements
                if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") {
                    groups = tokenize(selector);

                    if ((old = context.getAttribute("id"))) {
                        nid = old.replace(rescape, "\\$&");
                    } else {
                        context.setAttribute("id", nid);
                    }
                    nid = "[id='" + nid + "'] ";

                    i = groups.length;
                    while (i--) {
                        groups[i] = nid + toSelector(groups[i]);
                    }
                    newContext = rsibling.test(selector) && context.parentNode || context;
                    newSelector = groups.join(",");
                }

                if (newSelector) {
                    try {
                        push.apply(results,
                            newContext.querySelectorAll(newSelector)
                        );
                        return results;
                    } catch (qsaError) {
                    } finally {
                        if (!old) {
                            context.removeAttribute("id");
                        }
                    }
                }
            }
        }

        // All others
        return select(selector.replace(rtrim, "$1"), context, results, seed);
    }

    /**
     * Create key-value caches of limited size
     * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
     *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
     *	deleting the oldest entry
     */
    function createCache() {
        var keys = [];

        function cache(key, value) {
            // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
            if (keys.push(key += " ") > Expr.cacheLength) {
                // Only keep the most recent entries
                delete cache[keys.shift()];
            }
            return (cache[key] = value);
        }
        return cache;
    }

    /**
     * Mark a function for special use by Sizzle
     * @param {Function} fn The function to mark
     */
    function markFunction(fn) {
        fn[expando] = true;
        return fn;
    }

    /**
     * Support testing using an element
     * @param {Function} fn Passed the created div and expects a boolean result
     */
    function assert(fn) {
        var div = document.createElement("div");

        try {
            return !!fn(div);
        } catch (e) {
            return false;
        } finally {
            // Remove from its parent by default
            if (div.parentNode) {
                div.parentNode.removeChild(div);
            }
            // release memory in IE
            div = null;
        }
    }

    /**
     * Adds the same handler for all of the specified attrs
     * @param {String} attrs Pipe-separated list of attributes
     * @param {Function} handler The method that will be applied
     */
    function addHandle(attrs, handler) {
        var arr = attrs.split("|"),
            i = attrs.length;

        while (i--) {
            Expr.attrHandle[arr[i]] = handler;
        }
    }

    /**
     * Checks document order of two siblings
     * @param {Element} a
     * @param {Element} b
     * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
     */
    function siblingCheck(a, b) {
        var cur = b && a,
            diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
                (~b.sourceIndex || MAX_NEGATIVE) -
                (~a.sourceIndex || MAX_NEGATIVE);

        // Use IE sourceIndex if available on both nodes
        if (diff) {
            return diff;
        }

        // Check if b follows a
        if (cur) {
            while ((cur = cur.nextSibling)) {
                if (cur === b) {
                    return -1;
                }
            }
        }

        return a ? 1 : -1;
    }

    /**
     * Returns a function to use in pseudos for input types
     * @param {String} type
     */
    function createInputPseudo(type) {
        return function (elem) {
            var name = elem.nodeName.toLowerCase();
            return name === "input" && elem.type === type;
        };
    }

    /**
     * Returns a function to use in pseudos for buttons
     * @param {String} type
     */
    function createButtonPseudo(type) {
        return function (elem) {
            var name = elem.nodeName.toLowerCase();
            return (name === "input" || name === "button") && elem.type === type;
        };
    }

    /**
     * Returns a function to use in pseudos for positionals
     * @param {Function} fn
     */
    function createPositionalPseudo(fn) {
        return markFunction(function (argument) {
            argument = +argument;
            return markFunction(function (seed, matches) {
                var j,
                    matchIndexes = fn([], seed.length, argument),
                    i = matchIndexes.length;

                // Match elements found at the specified indexes
                while (i--) {
                    if (seed[(j = matchIndexes[i])]) {
                        seed[j] = !(matches[j] = seed[j]);
                    }
                }
            });
        });
    }

    /**
     * Detect xml
     * @param {Element|Object} elem An element or a document
     */
    isXML = Sizzle.isXML = function (elem) {
        // documentElement is verified for cases where it doesn't yet exist
        // (such as loading iframes in IE - #4833)
        var documentElement = elem && (elem.ownerDocument || elem).documentElement;
        return documentElement ? documentElement.nodeName !== "HTML" : false;
    };

    // Expose support vars for convenience
    support = Sizzle.support = {};

    /**
     * Sets document-related variables once based on the current document
     * @param {Element|Object} [doc] An element or document object to use to set the document
     * @returns {Object} Returns the current document
     */
    setDocument = Sizzle.setDocument = function (node) {
        var doc = node ? node.ownerDocument || node : preferredDoc,
            parent = doc.defaultView;

        // If no document and documentElement is available, return
        if (doc === document || doc.nodeType !== 9 || !doc.documentElement) {
            return document;
        }

        // Set our document
        document = doc;
        docElem = doc.documentElement;

        // Support tests
        documentIsHTML = !isXML(doc);

        // Support: IE>8
        // If iframe document is assigned to "document" variable and if iframe has been reloaded,
        // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
        // IE6-8 do not support the defaultView property so parent will be undefined
        if (parent && parent.attachEvent && parent !== parent.top) {
            parent.attachEvent("onbeforeunload", function () {
                setDocument();
            });
        }

        /* Attributes
        ---------------------------------------------------------------------- */

        // Support: IE<8
        // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
        support.attributes = assert(function (div) {
            div.className = "i";
            return !div.getAttribute("className");
        });

        /* getElement(s)By*
        ---------------------------------------------------------------------- */

        // Check if getElementsByTagName("*") returns only elements
        support.getElementsByTagName = assert(function (div) {
            div.appendChild(doc.createComment(""));
            return !div.getElementsByTagName("*").length;
        });

        // Check if getElementsByClassName can be trusted
        support.getElementsByClassName = assert(function (div) {
            div.innerHTML = "<div class='a'></div><div class='a i'></div>";

            // Support: Safari<4
            // Catch class over-caching
            div.firstChild.className = "i";
            // Support: Opera<10
            // Catch gEBCN failure to find non-leading classes
            return div.getElementsByClassName("i").length === 2;
        });

        // Support: IE<10
        // Check if getElementById returns elements by name
        // The broken getElementById methods don't pick up programatically-set names,
        // so use a roundabout getElementsByName test
        support.getById = assert(function (div) {
            docElem.appendChild(div).id = expando;
            return !doc.getElementsByName || !doc.getElementsByName(expando).length;
        });

        // ID find and filter
        if (support.getById) {
            Expr.find["ID"] = function (id, context) {
                if (typeof context.getElementById !== strundefined && documentIsHTML) {
                    var m = context.getElementById(id);
                    // Check parentNode to catch when Blackberry 4.6 returns
                    // nodes that are no longer in the document #6963
                    return m && m.parentNode ? [m] : [];
                }
            };
            Expr.filter["ID"] = function (id) {
                var attrId = id.replace(runescape, funescape);
                return function (elem) {
                    return elem.getAttribute("id") === attrId;
                };
            };
        } else {
            // Support: IE6/7
            // getElementById is not reliable as a find shortcut
            delete Expr.find["ID"];

            Expr.filter["ID"] = function (id) {
                var attrId = id.replace(runescape, funescape);
                return function (elem) {
                    var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
                    return node && node.value === attrId;
                };
            };
        }

        // Tag
        Expr.find["TAG"] = support.getElementsByTagName ?
            function (tag, context) {
                if (typeof context.getElementsByTagName !== strundefined) {
                    return context.getElementsByTagName(tag);
                }
            } :
            function (tag, context) {
                var elem,
                    tmp = [],
                    i = 0,
                    results = context.getElementsByTagName(tag);

                // Filter out possible comments
                if (tag === "*") {
                    while ((elem = results[i++])) {
                        if (elem.nodeType === 1) {
                            tmp.push(elem);
                        }
                    }

                    return tmp;
                }
                return results;
            };

        // Class
        Expr.find["CLASS"] = support.getElementsByClassName && function (className, context) {
            if (typeof context.getElementsByClassName !== strundefined && documentIsHTML) {
                return context.getElementsByClassName(className);
            }
        };

        /* QSA/matchesSelector
        ---------------------------------------------------------------------- */

        // QSA and matchesSelector support

        // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
        rbuggyMatches = [];

        // qSa(:focus) reports false when true (Chrome 21)
        // We allow this because of a bug in IE8/9 that throws an error
        // whenever `document.activeElement` is accessed on an iframe
        // So, we allow :focus to pass through QSA all the time to avoid the IE error
        // See http://bugs.jquery.com/ticket/13378
        rbuggyQSA = [];

        if ((support.qsa = rnative.test(doc.querySelectorAll))) {
            // Build QSA regex
            // Regex strategy adopted from Diego Perini
            assert(function (div) {
                // Select is set to empty string on purpose
                // This is to test IE's treatment of not explicitly
                // setting a boolean content attribute,
                // since its presence should be enough
                // http://bugs.jquery.com/ticket/12359
                div.innerHTML = "<select><option selected=''></option></select>";

                // Support: IE8
                // Boolean attributes and "value" are not treated correctly
                if (!div.querySelectorAll("[selected]").length) {
                    rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")");
                }

                // Webkit/Opera - :checked should return selected option elements
                // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
                // IE8 throws error here and will not see later tests
                if (!div.querySelectorAll(":checked").length) {
                    rbuggyQSA.push(":checked");
                }
            });

            assert(function (div) {

                // Support: Opera 10-12/IE8
                // ^= $= *= and empty values
                // Should not select anything
                // Support: Windows 8 Native Apps
                // The type attribute is restricted during .innerHTML assignment
                var input = doc.createElement("input");
                input.setAttribute("type", "hidden");
                div.appendChild(input).setAttribute("t", "");

                if (div.querySelectorAll("[t^='']").length) {
                    rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")");
                }

                // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
                // IE8 throws error here and will not see later tests
                if (!div.querySelectorAll(":enabled").length) {
                    rbuggyQSA.push(":enabled", ":disabled");
                }

                // Opera 10-11 does not throw on post-comma invalid pseudos
                div.querySelectorAll("*,:x");
                rbuggyQSA.push(",.*:");
            });
        }

        if ((support.matchesSelector = rnative.test((matches = docElem.webkitMatchesSelector ||
            docElem.mozMatchesSelector ||
            docElem.oMatchesSelector ||
            docElem.msMatchesSelector)))) {

            assert(function (div) {
                // Check to see if it's possible to do matchesSelector
                // on a disconnected node (IE 9)
                support.disconnectedMatch = matches.call(div, "div");

                // This should fail with an exception
                // Gecko does not error, returns false instead
                matches.call(div, "[s!='']:x");
                rbuggyMatches.push("!=", pseudos);
            });
        }

        rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|"));
        rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|"));

        /* Contains
        ---------------------------------------------------------------------- */

        // Element contains another
        // Purposefully does not implement inclusive descendent
        // As in, an element does not contain itself
        contains = rnative.test(docElem.contains) || docElem.compareDocumentPosition ?
            function (a, b) {
                var adown = a.nodeType === 9 ? a.documentElement : a,
                    bup = b && b.parentNode;
                return a === bup || !!(bup && bup.nodeType === 1 && (
                    adown.contains ?
                        adown.contains(bup) :
                        a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16
                ));
            } :
            function (a, b) {
                if (b) {
                    while ((b = b.parentNode)) {
                        if (b === a) {
                            return true;
                        }
                    }
                }
                return false;
            };

        /* Sorting
        ---------------------------------------------------------------------- */

        // Document order sorting
        sortOrder = docElem.compareDocumentPosition ?
        function (a, b) {

            // Flag for duplicate removal
            if (a === b) {
                hasDuplicate = true;
                return 0;
            }

            var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition(b);

            if (compare) {
                // Disconnected nodes
                if (compare & 1 ||
                    (!support.sortDetached && b.compareDocumentPosition(a) === compare)) {

                    // Choose the first element that is related to our preferred document
                    if (a === doc || contains(preferredDoc, a)) {
                        return -1;
                    }
                    if (b === doc || contains(preferredDoc, b)) {
                        return 1;
                    }

                    // Maintain original order
                    return sortInput ?
                        (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) :
                        0;
                }

                return compare & 4 ? -1 : 1;
            }

            // Not directly comparable, sort on existence of method
            return a.compareDocumentPosition ? -1 : 1;
        } :
        function (a, b) {
            var cur,
                i = 0,
                aup = a.parentNode,
                bup = b.parentNode,
                ap = [a],
                bp = [b];

            // Exit early if the nodes are identical
            if (a === b) {
                hasDuplicate = true;
                return 0;

                // Parentless nodes are either documents or disconnected
            } else if (!aup || !bup) {
                return a === doc ? -1 :
                    b === doc ? 1 :
                    aup ? -1 :
                    bup ? 1 :
                    sortInput ?
                    (indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) :
                    0;

                // If the nodes are siblings, we can do a quick check
            } else if (aup === bup) {
                return siblingCheck(a, b);
            }

            // Otherwise we need full lists of their ancestors for comparison
            cur = a;
            while ((cur = cur.parentNode)) {
                ap.unshift(cur);
            }
            cur = b;
            while ((cur = cur.parentNode)) {
                bp.unshift(cur);
            }

            // Walk down the tree looking for a discrepancy
            while (ap[i] === bp[i]) {
                i++;
            }

            return i ?
                // Do a sibling check if the nodes have a common ancestor
                siblingCheck(ap[i], bp[i]) :

                // Otherwise nodes in our document sort first
                ap[i] === preferredDoc ? -1 :
                bp[i] === preferredDoc ? 1 :
                0;
        };

        return doc;
    };

    Sizzle.matches = function (expr, elements) {
        return Sizzle(expr, null, null, elements);
    };

    Sizzle.matchesSelector = function (elem, expr) {
        // Set document vars if needed
        if ((elem.ownerDocument || elem) !== document) {
            setDocument(elem);
        }

        // Make sure that attribute selectors are quoted
        expr = expr.replace(rattributeQuotes, "='$1']");

        if (support.matchesSelector && documentIsHTML &&
            (!rbuggyMatches || !rbuggyMatches.test(expr)) &&
            (!rbuggyQSA || !rbuggyQSA.test(expr))) {

            try {
                var ret = matches.call(elem, expr);

                // IE 9's matchesSelector returns false on disconnected nodes
                if (ret || support.disconnectedMatch ||
                    // As well, disconnected nodes are said to be in a document
                    // fragment in IE 9
                        elem.document && elem.document.nodeType !== 11) {
                    return ret;
                }
            } catch (e) { }
        }

        return Sizzle(expr, document, null, [elem]).length > 0;
    };

    Sizzle.contains = function (context, elem) {
        // Set document vars if needed
        if ((context.ownerDocument || context) !== document) {
            setDocument(context);
        }
        return contains(context, elem);
    };

    Sizzle.attr = function (elem, name) {
        // Set document vars if needed
        if ((elem.ownerDocument || elem) !== document) {
            setDocument(elem);
        }

        var fn = Expr.attrHandle[name.toLowerCase()],
            // Don't get fooled by Object.prototype properties (jQuery #13807)
            val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ?
                fn(elem, name, !documentIsHTML) :
                undefined;

        return val === undefined ?
            support.attributes || !documentIsHTML ?
                elem.getAttribute(name) :
                (val = elem.getAttributeNode(name)) && val.specified ?
                    val.value :
                    null :
            val;
    };

    Sizzle.error = function (msg) {
        throw new Error("Syntax error, unrecognized expression: " + msg);
    };

    /**
     * Document sorting and removing duplicates
     * @param {ArrayLike} results
     */
    Sizzle.uniqueSort = function (results) {
        var elem,
            duplicates = [],
            j = 0,
            i = 0;

        // Unless we *know* we can detect duplicates, assume their presence
        hasDuplicate = !support.detectDuplicates;
        sortInput = !support.sortStable && results.slice(0);
        results.sort(sortOrder);

        if (hasDuplicate) {
            while ((elem = results[i++])) {
                if (elem === results[i]) {
                    j = duplicates.push(i);
                }
            }
            while (j--) {
                results.splice(duplicates[j], 1);
            }
        }

        return results;
    };

    /**
     * Utility function for retrieving the text value of an array of DOM nodes
     * @param {Array|Element} elem
     */
    getText = Sizzle.getText = function (elem) {
        var node,
            ret = "",
            i = 0,
            nodeType = elem.nodeType;

        if (!nodeType) {
            // If no nodeType, this is expected to be an array
            for (; (node = elem[i]) ; i++) {
                // Do not traverse comment nodes
                ret += getText(node);
            }
        } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
            // Use textContent for elements
            // innerText usage removed for consistency of new lines (see #11153)
            if (typeof elem.textContent === "string") {
                return elem.textContent;
            } else {
                // Traverse its children
                for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
                    ret += getText(elem);
                }
            }
        } else if (nodeType === 3 || nodeType === 4) {
            return elem.nodeValue;
        }
        // Do not include comment or processing instruction nodes

        return ret;
    };

    Expr = Sizzle.selectors = {

        // Can be adjusted by the user
        cacheLength: 50,

        createPseudo: markFunction,

        match: matchExpr,

        attrHandle: {},

        find: {},

        relative: {
            ">": { dir: "parentNode", first: true },
            " ": { dir: "parentNode" },
            "+": { dir: "previousSibling", first: true },
            "~": { dir: "previousSibling" }
        },

        preFilter: {
            "ATTR": function (match) {
                match[1] = match[1].replace(runescape, funescape);

                // Move the given value to match[3] whether quoted or unquoted
                match[3] = (match[4] || match[5] || "").replace(runescape, funescape);

                if (match[2] === "~=") {
                    match[3] = " " + match[3] + " ";
                }

                return match.slice(0, 4);
            },

            "CHILD": function (match) {
                /* matches from matchExpr["CHILD"]
                    1 type (only|nth|...)
                    2 what (child|of-type)
                    3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
                    4 xn-component of xn+y argument ([+-]?\d*n|)
                    5 sign of xn-component
                    6 x of xn-component
                    7 sign of y-component
                    8 y of y-component
                */
                match[1] = match[1].toLowerCase();

                if (match[1].slice(0, 3) === "nth") {
                    // nth-* requires argument
                    if (!match[3]) {
                        Sizzle.error(match[0]);
                    }

                    // numeric x and y parameters for Expr.filter.CHILD
                    // remember that false/true cast respectively to 0/1
                    match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd"));
                    match[5] = +((match[7] + match[8]) || match[3] === "odd");

                    // other types prohibit arguments
                } else if (match[3]) {
                    Sizzle.error(match[0]);
                }

                return match;
            },

            "PSEUDO": function (match) {
                var excess,
                    unquoted = !match[5] && match[2];

                if (matchExpr["CHILD"].test(match[0])) {
                    return null;
                }

                // Accept quoted arguments as-is
                if (match[3] && match[4] !== undefined) {
                    match[2] = match[4];

                    // Strip excess characters from unquoted arguments
                } else if (unquoted && rpseudo.test(unquoted) &&
                    // Get excess from tokenize (recursively)
                    (excess = tokenize(unquoted, true)) &&
                    // advance to the next closing parenthesis
                    (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) {

                    // excess is a negative index
                    match[0] = match[0].slice(0, excess);
                    match[2] = unquoted.slice(0, excess);
                }

                // Return only captures needed by the pseudo filter method (type and argument)
                return match.slice(0, 3);
            }
        },

        filter: {

            "TAG": function (nodeNameSelector) {
                var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();
                return nodeNameSelector === "*" ?
                    function () { return true; } :
                    function (elem) {
                        return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
                    };
            },

            "CLASS": function (className) {
                var pattern = classCache[className + " "];

                return pattern ||
                    (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) &&
                    classCache(className, function (elem) {
                        return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "");
                    });
            },

            "ATTR": function (name, operator, check) {
                return function (elem) {
                    var result = Sizzle.attr(elem, name);

                    if (result == null) {
                        return operator === "!=";
                    }
                    if (!operator) {
                        return true;
                    }

                    result += "";

                    return operator === "=" ? result === check :
                        operator === "!=" ? result !== check :
                        operator === "^=" ? check && result.indexOf(check) === 0 :
                        operator === "*=" ? check && result.indexOf(check) > -1 :
                        operator === "$=" ? check && result.slice(-check.length) === check :
                        operator === "~=" ? (" " + result + " ").indexOf(check) > -1 :
                        operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" :
                        false;
                };
            },

            "CHILD": function (type, what, argument, first, last) {
                var simple = type.slice(0, 3) !== "nth",
                    forward = type.slice(-4) !== "last",
                    ofType = what === "of-type";

                return first === 1 && last === 0 ?

                    // Shortcut for :nth-*(n)
                    function (elem) {
                        return !!elem.parentNode;
                    } :

                    function (elem, context, xml) {
                        var cache, outerCache, node, diff, nodeIndex, start,
                            dir = simple !== forward ? "nextSibling" : "previousSibling",
                            parent = elem.parentNode,
                            name = ofType && elem.nodeName.toLowerCase(),
                            useCache = !xml && !ofType;

                        if (parent) {

                            // :(first|last|only)-(child|of-type)
                            if (simple) {
                                while (dir) {
                                    node = elem;
                                    while ((node = node[dir])) {
                                        if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) {
                                            return false;
                                        }
                                    }
                                    // Reverse direction for :only-* (if we haven't yet done so)
                                    start = dir = type === "only" && !start && "nextSibling";
                                }
                                return true;
                            }

                            start = [forward ? parent.firstChild : parent.lastChild];

                            // non-xml :nth-child(...) stores cache data on `parent`
                            if (forward && useCache) {
                                // Seek `elem` from a previously-cached index
                                outerCache = parent[expando] || (parent[expando] = {});
                                cache = outerCache[type] || [];
                                nodeIndex = cache[0] === dirruns && cache[1];
                                diff = cache[0] === dirruns && cache[2];
                                node = nodeIndex && parent.childNodes[nodeIndex];

                                while ((node = ++nodeIndex && node && node[dir] ||

                                    // Fallback to seeking `elem` from the start
                                    (diff = nodeIndex = 0) || start.pop())) {

                                    // When found, cache indexes on `parent` and break
                                    if (node.nodeType === 1 && ++diff && node === elem) {
                                        outerCache[type] = [dirruns, nodeIndex, diff];
                                        break;
                                    }
                                }

                                // Use previously-cached element index if available
                            } else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) {
                                diff = cache[1];

                                // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
                            } else {
                                // Use the same loop as above to seek `elem` from the start
                                while ((node = ++nodeIndex && node && node[dir] ||
                                    (diff = nodeIndex = 0) || start.pop())) {

                                    if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) {
                                        // Cache the index of each encountered element
                                        if (useCache) {
                                            (node[expando] || (node[expando] = {}))[type] = [dirruns, diff];
                                        }

                                        if (node === elem) {
                                            break;
                                        }
                                    }
                                }
                            }

                            // Incorporate the offset, then check against cycle size
                            diff -= last;
                            return diff === first || (diff % first === 0 && diff / first >= 0);
                        }
                    };
            },

            "PSEUDO": function (pseudo, argument) {
                // pseudo-class names are case-insensitive
                // http://www.w3.org/TR/selectors/#pseudo-classes
                // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
                // Remember that setFilters inherits from pseudos
                var args,
                    fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] ||
                        Sizzle.error("unsupported pseudo: " + pseudo);

                // The user may use createPseudo to indicate that
                // arguments are needed to create the filter function
                // just as Sizzle does
                if (fn[expando]) {
                    return fn(argument);
                }

                // But maintain support for old signatures
                if (fn.length > 1) {
                    args = [pseudo, pseudo, "", argument];
                    return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ?
                        markFunction(function (seed, matches) {
                            var idx,
                                matched = fn(seed, argument),
                                i = matched.length;
                            while (i--) {
                                idx = indexOf.call(seed, matched[i]);
                                seed[idx] = !(matches[idx] = matched[i]);
                            }
                        }) :
                        function (elem) {
                            return fn(elem, 0, args);
                        };
                }

                return fn;
            }
        },

        pseudos: {
            // Potentially complex pseudos
            "not": markFunction(function (selector) {
                // Trim the selector passed to compile
                // to avoid treating leading and trailing
                // spaces as combinators
                var input = [],
                    results = [],
                    matcher = compile(selector.replace(rtrim, "$1"));

                return matcher[expando] ?
                    markFunction(function (seed, matches, context, xml) {
                        var elem,
                            unmatched = matcher(seed, null, xml, []),
                            i = seed.length;

                        // Match elements unmatched by `matcher`
                        while (i--) {
                            if ((elem = unmatched[i])) {
                                seed[i] = !(matches[i] = elem);
                            }
                        }
                    }) :
                    function (elem, context, xml) {
                        input[0] = elem;
                        matcher(input, null, xml, results);
                        return !results.pop();
                    };
            }),

            "has": markFunction(function (selector) {
                return function (elem) {
                    return Sizzle(selector, elem).length > 0;
                };
            }),

            "contains": markFunction(function (text) {
                return function (elem) {
                    return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1;
                };
            }),

            // "Whether an element is represented by a :lang() selector
            // is based solely on the element's language value
            // being equal to the identifier C,
            // or beginning with the identifier C immediately followed by "-".
            // The matching of C against the element's language value is performed case-insensitively.
            // The identifier C does not have to be a valid language name."
            // http://www.w3.org/TR/selectors/#lang-pseudo
            "lang": markFunction(function (lang) {
                // lang value must be a valid identifier
                if (!ridentifier.test(lang || "")) {
                    Sizzle.error("unsupported lang: " + lang);
                }
                lang = lang.replace(runescape, funescape).toLowerCase();
                return function (elem) {
                    var elemLang;
                    do {
                        if ((elemLang = documentIsHTML ?
                            elem.lang :
                            elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) {

                            elemLang = elemLang.toLowerCase();
                            return elemLang === lang || elemLang.indexOf(lang + "-") === 0;
                        }
                    } while ((elem = elem.parentNode) && elem.nodeType === 1);
                    return false;
                };
            }),

            // Miscellaneous
            "target": function (elem) {
                var hash = window.location && window.location.hash;
                return hash && hash.slice(1) === elem.id;
            },

            "root": function (elem) {
                return elem === docElem;
            },

            "focus": function (elem) {
                return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
            },

            // Boolean properties
            "enabled": function (elem) {
                return elem.disabled === false;
            },

            "disabled": function (elem) {
                return elem.disabled === true;
            },

            "checked": function (elem) {
                // In CSS3, :checked should return both checked and selected elements
                // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
                var nodeName = elem.nodeName.toLowerCase();
                return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
            },

            "selected": function (elem) {
                // Accessing this property makes selected-by-default
                // options in Safari work properly
                if (elem.parentNode) {
                    elem.parentNode.selectedIndex;
                }

                return elem.selected === true;
            },

            // Contents
            "empty": function (elem) {
                // http://www.w3.org/TR/selectors/#empty-pseudo
                // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
                //   not comment, processing instructions, or others
                // Thanks to Diego Perini for the nodeName shortcut
                //   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
                for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
                    if (elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4) {
                        return false;
                    }
                }
                return true;
            },

            "parent": function (elem) {
                return !Expr.pseudos["empty"](elem);
            },

            // Element/input types
            "header": function (elem) {
                return rheader.test(elem.nodeName);
            },

            "input": function (elem) {
                return rinputs.test(elem.nodeName);
            },

            "button": function (elem) {
                var name = elem.nodeName.toLowerCase();
                return name === "input" && elem.type === "button" || name === "button";
            },

            "text": function (elem) {
                var attr;
                // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
                // use getAttribute instead to test this case
                return elem.nodeName.toLowerCase() === "input" &&
                    elem.type === "text" &&
                    ((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type);
            },

            // Position-in-collection
            "first": createPositionalPseudo(function () {
                return [0];
            }),

            "last": createPositionalPseudo(function (matchIndexes, length) {
                return [length - 1];
            }),

            "eq": createPositionalPseudo(function (matchIndexes, length, argument) {
                return [argument < 0 ? argument + length : argument];
            }),

            "even": createPositionalPseudo(function (matchIndexes, length) {
                var i = 0;
                for (; i < length; i += 2) {
                    matchIndexes.push(i);
                }
                return matchIndexes;
            }),

            "odd": createPositionalPseudo(function (matchIndexes, length) {
                var i = 1;
                for (; i < length; i += 2) {
                    matchIndexes.push(i);
                }
                return matchIndexes;
            }),

            "lt": createPositionalPseudo(function (matchIndexes, length, argument) {
                var i = argument < 0 ? argument + length : argument;
                for (; --i >= 0;) {
                    matchIndexes.push(i);
                }
                return matchIndexes;
            }),

            "gt": createPositionalPseudo(function (matchIndexes, length, argument) {
                var i = argument < 0 ? argument + length : argument;
                for (; ++i < length;) {
                    matchIndexes.push(i);
                }
                return matchIndexes;
            })
        }
    };

    Expr.pseudos["nth"] = Expr.pseudos["eq"];

    // Add button/input type pseudos
    for (i in { radio: true, checkbox: true, file: true, password: true, image: true }) {
        Expr.pseudos[i] = createInputPseudo(i);
    }
    for (i in { submit: true, reset: true }) {
        Expr.pseudos[i] = createButtonPseudo(i);
    }

    // Easy API for creating new setFilters
    function setFilters() { }
    setFilters.prototype = Expr.filters = Expr.pseudos;
    Expr.setFilters = new setFilters();

    function tokenize(selector, parseOnly) {
        var matched, match, tokens, type,
            soFar, groups, preFilters,
            cached = tokenCache[selector + " "];

        if (cached) {
            return parseOnly ? 0 : cached.slice(0);
        }

        soFar = selector;
        groups = [];
        preFilters = Expr.preFilter;

        while (soFar) {

            // Comma and first run
            if (!matched || (match = rcomma.exec(soFar))) {
                if (match) {
                    // Don't consume trailing commas as valid
                    soFar = soFar.slice(match[0].length) || soFar;
                }
                groups.push(tokens = []);
            }

            matched = false;

            // Combinators
            if ((match = rcombinators.exec(soFar))) {
                matched = match.shift();
                tokens.push({
                    value: matched,
                    // Cast descendant combinators to space
                    type: match[0].replace(rtrim, " ")
                });
                soFar = soFar.slice(matched.length);
            }

            // Filters
            for (type in Expr.filter) {
                if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] ||
                    (match = preFilters[type](match)))) {
                    matched = match.shift();
                    tokens.push({
                        value: matched,
                        type: type,
                        matches: match
                    });
                    soFar = soFar.slice(matched.length);
                }
            }

            if (!matched) {
                break;
            }
        }

        // Return the length of the invalid excess
        // if we're just parsing
        // Otherwise, throw an error or return tokens
        return parseOnly ?
            soFar.length :
            soFar ?
                Sizzle.error(selector) :
                // Cache the tokens
                tokenCache(selector, groups).slice(0);
    }

    function toSelector(tokens) {
        var i = 0,
            len = tokens.length,
            selector = "";
        for (; i < len; i++) {
            selector += tokens[i].value;
        }
        return selector;
    }

    function addCombinator(matcher, combinator, base) {
        var dir = combinator.dir,
            checkNonElements = base && dir === "parentNode",
            doneName = done++;

        return combinator.first ?
            // Check against closest ancestor/preceding element
            function (elem, context, xml) {
                while ((elem = elem[dir])) {
                    if (elem.nodeType === 1 || checkNonElements) {
                        return matcher(elem, context, xml);
                    }
                }
            } :

            // Check against all ancestor/preceding elements
            function (elem, context, xml) {
                var data, cache, outerCache,
                    dirkey = dirruns + " " + doneName;

                // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
                if (xml) {
                    while ((elem = elem[dir])) {
                        if (elem.nodeType === 1 || checkNonElements) {
                            if (matcher(elem, context, xml)) {
                                return true;
                            }
                        }
                    }
                } else {
                    while ((elem = elem[dir])) {
                        if (elem.nodeType === 1 || checkNonElements) {
                            outerCache = elem[expando] || (elem[expando] = {});
                            if ((cache = outerCache[dir]) && cache[0] === dirkey) {
                                if ((data = cache[1]) === true || data === cachedruns) {
                                    return data === true;
                                }
                            } else {
                                cache = outerCache[dir] = [dirkey];
                                cache[1] = matcher(elem, context, xml) || cachedruns;
                                if (cache[1] === true) {
                                    return true;
                                }
                            }
                        }
                    }
                }
            };
    }

    function elementMatcher(matchers) {
        return matchers.length > 1 ?
            function (elem, context, xml) {
                var i = matchers.length;
                while (i--) {
                    if (!matchers[i](elem, context, xml)) {
                        return false;
                    }
                }
                return true;
            } :
            matchers[0];
    }

    function condense(unmatched, map, filter, context, xml) {
        var elem,
            newUnmatched = [],
            i = 0,
            len = unmatched.length,
            mapped = map != null;

        for (; i < len; i++) {
            if ((elem = unmatched[i])) {
                if (!filter || filter(elem, context, xml)) {
                    newUnmatched.push(elem);
                    if (mapped) {
                        map.push(i);
                    }
                }
            }
        }

        return newUnmatched;
    }

    function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) {
        if (postFilter && !postFilter[expando]) {
            postFilter = setMatcher(postFilter);
        }
        if (postFinder && !postFinder[expando]) {
            postFinder = setMatcher(postFinder, postSelector);
        }
        return markFunction(function (seed, results, context, xml) {
            var temp, i, elem,
                preMap = [],
                postMap = [],
                preexisting = results.length,

                // Get initial elements from seed or context
                elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []),

                // Prefilter to get matcher input, preserving a map for seed-results synchronization
                matcherIn = preFilter && (seed || !selector) ?
                    condense(elems, preMap, preFilter, context, xml) :
                    elems,

                matcherOut = matcher ?
                    // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
                    postFinder || (seed ? preFilter : preexisting || postFilter) ?

                        // ...intermediate processing is necessary
                        [] :

                        // ...otherwise use results directly
                results :
                    matcherIn;

            // Find primary matches
            if (matcher) {
                matcher(matcherIn, matcherOut, context, xml);
            }

            // Apply postFilter
            if (postFilter) {
                temp = condense(matcherOut, postMap);
                postFilter(temp, [], context, xml);

                // Un-match failing elements by moving them back to matcherIn
                i = temp.length;
                while (i--) {
                    if ((elem = temp[i])) {
                        matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);
                    }
                }
            }

            if (seed) {
                if (postFinder || preFilter) {
                    if (postFinder) {
                        // Get the final matcherOut by condensing this intermediate into postFinder contexts
                        temp = [];
                        i = matcherOut.length;
                        while (i--) {
                            if ((elem = matcherOut[i])) {
                                // Restore matcherIn since elem is not yet a final match
                                temp.push((matcherIn[i] = elem));
                            }
                        }
                        postFinder(null, (matcherOut = []), temp, xml);
                    }

                    // Move matched elements from seed to results to keep them synchronized
                    i = matcherOut.length;
                    while (i--) {
                        if ((elem = matcherOut[i]) &&
                            (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) {

                            seed[temp] = !(results[temp] = elem);
                        }
                    }
                }

                // Add elements to results, through postFinder if defined
            } else {
                matcherOut = condense(
                    matcherOut === results ?
                        matcherOut.splice(preexisting, matcherOut.length) :
                        matcherOut
                );
                if (postFinder) {
                    postFinder(null, results, matcherOut, xml);
                } else {
                    push.apply(results, matcherOut);
                }
            }
        });
    }

    function matcherFromTokens(tokens) {
        var checkContext, matcher, j,
            len = tokens.length,
            leadingRelative = Expr.relative[tokens[0].type],
            implicitRelative = leadingRelative || Expr.relative[" "],
            i = leadingRelative ? 1 : 0,

            // The foundational matcher ensures that elements are reachable from top-level context(s)
            matchContext = addCombinator(function (elem) {
                return elem === checkContext;
            }, implicitRelative, true),
            matchAnyContext = addCombinator(function (elem) {
                return indexOf.call(checkContext, elem) > -1;
            }, implicitRelative, true),
            matchers = [function (elem, context, xml) {
                return (!leadingRelative && (xml || context !== outermostContext)) || (
                    (checkContext = context).nodeType ?
                        matchContext(elem, context, xml) :
                        matchAnyContext(elem, context, xml));
            }];

        for (; i < len; i++) {
            if ((matcher = Expr.relative[tokens[i].type])) {
                matchers = [addCombinator(elementMatcher(matchers), matcher)];
            } else {
                matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches);

                // Return special upon seeing a positional matcher
                if (matcher[expando]) {
                    // Find the next relative operator (if any) for proper handling
                    j = ++i;
                    for (; j < len; j++) {
                        if (Expr.relative[tokens[j].type]) {
                            break;
                        }
                    }
                    return setMatcher(
                        i > 1 && elementMatcher(matchers),
                        i > 1 && toSelector(
                            // If the preceding token was a descendant combinator, insert an implicit any-element `*`
                            tokens.slice(0, i - 1).concat({ value: tokens[i - 2].type === " " ? "*" : "" })
                        ).replace(rtrim, "$1"),
                        matcher,
                        i < j && matcherFromTokens(tokens.slice(i, j)),
                        j < len && matcherFromTokens((tokens = tokens.slice(j))),
                        j < len && toSelector(tokens)
                    );
                }
                matchers.push(matcher);
            }
        }

        return elementMatcher(matchers);
    }

    function matcherFromGroupMatchers(elementMatchers, setMatchers) {
        // A counter to specify which element is currently being matched
        var matcherCachedRuns = 0,
            bySet = setMatchers.length > 0,
            byElement = elementMatchers.length > 0,
            superMatcher = function (seed, context, xml, results, expandContext) {
                var elem, j, matcher,
                    setMatched = [],
                    matchedCount = 0,
                    i = "0",
                    unmatched = seed && [],
                    outermost = expandContext != null,
                    contextBackup = outermostContext,
                    // We must always have either seed elements or context
                    elems = seed || byElement && Expr.find["TAG"]("*", expandContext && context.parentNode || context),
                    // Use integer dirruns iff this is the outermost matcher
                    dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);

                if (outermost) {
                    outermostContext = context !== document && context;
                    cachedruns = matcherCachedRuns;
                }

                // Add elements passing elementMatchers directly to results
                // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
                for (; (elem = elems[i]) != null; i++) {
                    if (byElement && elem) {
                        j = 0;
                        while ((matcher = elementMatchers[j++])) {
                            if (matcher(elem, context, xml)) {
                                results.push(elem);
                                break;
                            }
                        }
                        if (outermost) {
                            dirruns = dirrunsUnique;
                            cachedruns = ++matcherCachedRuns;
                        }
                    }

                    // Track unmatched elements for set filters
                    if (bySet) {
                        // They will have gone through all possible matchers
                        if ((elem = !matcher && elem)) {
                            matchedCount--;
                        }

                        // Lengthen the array for every element, matched or not
                        if (seed) {
                            unmatched.push(elem);
                        }
                    }
                }

                // Apply set filters to unmatched elements
                matchedCount += i;
                if (bySet && i !== matchedCount) {
                    j = 0;
                    while ((matcher = setMatchers[j++])) {
                        matcher(unmatched, setMatched, context, xml);
                    }

                    if (seed) {
                        // Reintegrate element matches to eliminate the need for sorting
                        if (matchedCount > 0) {
                            while (i--) {
                                if (!(unmatched[i] || setMatched[i])) {
                                    setMatched[i] = pop.call(results);
                                }
                            }
                        }

                        // Discard index placeholder values to get only actual matches
                        setMatched = condense(setMatched);
                    }

                    // Add matches to results
                    push.apply(results, setMatched);

                    // Seedless set matches succeeding multiple successful matchers stipulate sorting
                    if (outermost && !seed && setMatched.length > 0 &&
                        (matchedCount + setMatchers.length) > 1) {

                        Sizzle.uniqueSort(results);
                    }
                }

                // Override manipulation of globals by nested matchers
                if (outermost) {
                    dirruns = dirrunsUnique;
                    outermostContext = contextBackup;
                }

                return unmatched;
            };

        return bySet ?
            markFunction(superMatcher) :
            superMatcher;
    }

    compile = Sizzle.compile = function (selector, group /* Internal Use Only */) {
        var i,
            setMatchers = [],
            elementMatchers = [],
            cached = compilerCache[selector + " "];

        if (!cached) {
            // Generate a function of recursive functions that can be used to check each element
            if (!group) {
                group = tokenize(selector);
            }
            i = group.length;
            while (i--) {
                cached = matcherFromTokens(group[i]);
                if (cached[expando]) {
                    setMatchers.push(cached);
                } else {
                    elementMatchers.push(cached);
                }
            }

            // Cache the compiled function
            cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers));
        }
        return cached;
    };

    function multipleContexts(selector, contexts, results) {
        var i = 0,
            len = contexts.length;
        for (; i < len; i++) {
            Sizzle(selector, contexts[i], results);
        }
        return results;
    }

    function select(selector, context, results, seed) {
        var i, tokens, token, type, find,
            match = tokenize(selector);

        if (!seed) {
            // Try to minimize operations if there is only one group
            if (match.length === 1) {

                // Take a shortcut and set the context if the root selector is an ID
                tokens = match[0] = match[0].slice(0);
                if (tokens.length > 2 && (token = tokens[0]).type === "ID" &&
                        support.getById && context.nodeType === 9 && documentIsHTML &&
                        Expr.relative[tokens[1].type]) {

                    context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0];
                    if (!context) {
                        return results;
                    }
                    selector = selector.slice(tokens.shift().value.length);
                }

                // Fetch a seed set for right-to-left matching
                i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length;
                while (i--) {
                    token = tokens[i];

                    // Abort if we hit a combinator
                    if (Expr.relative[(type = token.type)]) {
                        break;
                    }
                    if ((find = Expr.find[type])) {
                        // Search, expanding context for leading sibling combinators
                        if ((seed = find(
                            token.matches[0].replace(runescape, funescape),
                            rsibling.test(tokens[0].type) && context.parentNode || context
                        ))) {

                            // If seed is empty or no tokens remain, we can return early
                            tokens.splice(i, 1);
                            selector = seed.length && toSelector(tokens);
                            if (!selector) {
                                push.apply(results, seed);
                                return results;
                            }

                            break;
                        }
                    }
                }
            }
        }

        // Compile and execute a filtering function
        // Provide `match` to avoid retokenization if we modified the selector above
        compile(selector, match)(
            seed,
            context,
            !documentIsHTML,
            results,
            rsibling.test(selector)
        );
        return results;
    }

    // One-time assignments

    // Sort stability
    support.sortStable = expando.split("").sort(sortOrder).join("") === expando;

    // Support: Chrome<14
    // Always assume duplicates if they aren't passed to the comparison function
    support.detectDuplicates = hasDuplicate;

    // Initialize against the default document
    setDocument();

    // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
    // Detached nodes confoundingly follow *each other*
    support.sortDetached = assert(function (div1) {
        // Should return 1, but returns 4 (following)
        return div1.compareDocumentPosition(document.createElement("div")) & 1;
    });

    // Support: IE<8
    // Prevent attribute/property "interpolation"
    // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
    if (!assert(function (div) {
        div.innerHTML = "<a href='#'></a>";
        return div.firstChild.getAttribute("href") === "#";
    })) {
        addHandle("type|href|height|width", function (elem, name, isXML) {
            if (!isXML) {
                return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2);
            }
        });
    }

    // Support: IE<9
    // Use defaultValue in place of getAttribute("value")
    if (!support.attributes || !assert(function (div) {
        div.innerHTML = "<input/>";
        div.firstChild.setAttribute("value", "");
        return div.firstChild.getAttribute("value") === "";
    })) {
        addHandle("value", function (elem, name, isXML) {
            if (!isXML && elem.nodeName.toLowerCase() === "input") {
                return elem.defaultValue;
            }
        });
    }

    // Support: IE<9
    // Use getAttributeNode to fetch booleans when getAttribute lies
    if (!assert(function (div) {
        return div.getAttribute("disabled") == null;
    })) {
        addHandle(booleans, function (elem, name, isXML) {
            var val;
            if (!isXML) {
                return (val = elem.getAttributeNode(name)) && val.specified ?
                    val.value :
                    elem[name] === true ? name.toLowerCase() : null;
            }
        });
    }

    // EXPOSE
    if (typeof define === "function" && define.amd) {
        define(function () { return Sizzle; });
    } else {
        //window.Sizzle = Sizzle;
        return Sizzle;
    }
    // EXPOSE

})(window);//dom选择函数


//任何传递给load的参数为函数，在文档准备就绪时调用，若文档已经就绪，马上调用
//this绑定到document
wkh.tools.ready = (function () {
    var funcs = [];//运行的函数列队
    var ready = false;//is the document ready

    var funcsAllDown = [];//所有资源加载完毕后执行
    var readyAllDown = false;//是否加载完毕

    //the event handler invoked when the document becomes ready
    function doFuncs(e,type){
        //if we've already run once, just return
        if (type && readyAllDown) {
            return;
        }
        if (!type && ready) {
            return;
        }

        //if this was a readystatechange event where the state changed to 
        //something other than "complete", then we're not ready yet
        if (e.type === "readystatechange" && document.readyState !== "complete") return;

        //set the ready flag to true
        var f;
        if (type) {
            readyAllDown = true;
            f = funcsAllDown;
        } else {
            ready = true;
            f = funcs;
        }
        

        //run all registered functions.
        for (var i = 0, l = f.length; i < l; ++i) {
            f[i].call(document);
        }

        //set the function array to null
        if (type) {
            funcsAllDown = [];
        } else {
            funcs = [];
        }
    }

    function handler(e) {
        e = e || window.event;
        doFuncs(e);
    }
    function handlerAllDown(e) {
        e = e || window.event;
        doFuncs(e, true);
    }

    //register the handler for any event we might receive
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", handler, false);
        document.addEventListener("readystatechange", handler, false);
        window.addEventListener("load", handler, false);

        window.addEventListener("load", handlerAllDown, false);
    } else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", handler);
        window.attachEvent("onload", handler);

        var e = new Error();
        setTimeout(function () {
            try {
                try {
                    document.body.doScroll('left');
                } catch (a) {
                    throw e;
                }
                handler({ type: "" });
            } catch (ex) {
                if (ex === e) {
                    setTimeout(arguments.callee, 10);
                } else {
                    throw ex;
                }
            }
        }, 10);

        window.attachEvent("onload", handlerAllDown);
    }

    //return the ready function
    return function (f,isAllDown) {
        if (isAllDown) {
            if (readyAllDown) f.call(document);
            else funcsAllDown.push(f);
        } else {
            if (ready) f.call(document);//if already ready, just run it
            else funcs.push(f);//otherwise,queue it for later
        }
    };
}());

wkh.tools.loadScript = function (url, node, fn) {//异步加载和执行脚本
    if (Object.classof(node) === "Function") {
        fn = node;
        node = null;
    } 

    node = node || (document.head || document.getElementsByTagName("head")[0]);

    var s = document.createElement("script");

    if (fn) {
        s.addEventListener("load", fn);
    }

    s.src = url;
    node.appendChild(s);
};

wkh.tools.loadCSS = function (url) {
    if (document.createStyleSheet) {
        document.createStyleSheet(url);
    } else {
        var node = document.head || document.getElementsByTagName("head")[0];
        var l = document.createElement("link");
        node.appendChild(l);
        l.setAttribute("rel", "stylesheet");
        l.setAttribute("type", "text/css");
        l.setAttribute("href", url);
    }
};

wkh.tools.addStyle = function (styles) {
    var styleElt, styleSheet;
    if (document.createStyleSheet) {
        styleSheet = document.createStyleSheet();
    } else {
        var node = document.head || document.getElementsByTagName("head")[0];
        styleElt = document.createElement("style");
        node.appendChild(styleElt);
        styleSheet = document.styleSheets[document.styleSheets.length - 1];
    }

    if (Object.prototype.toString.call(styles).slice(8, -1) === "String") {
        if (styleElt) styleElt.innerHTML = styles;
        else styleSheet.cssText = styles;
    } else {
        var i = 0, selector;
        for (selector in styles) {
            if (styleSheet.insertRule) {
                var rule = selector + "{" + styles[selector] + "}";
                styleSheet.insertRule(rule, i++);
            } else {
                styleSheet.addRule(selector, styles[selector], i++);
            }
        }
    }
};

wkh.tools.switchStylesheet = function (ss, method) {//控制样式表的开关
    if (ss==undefined) {
        return;
    }

    if (Object.prototype.toString.call(ss).slice(8, -1) === "Number") {
        switch (method) {
            case "close":
                document.styleSheets[ss].disabled = true;
                break;
            case "open":
                document.styleSheets[ss].disabled = false;
                break;
            default:
                document.styleSheets[ss].disabled = !document.styleSheets[ss].disabled;
                break;
        }
    } else {
        for (var i = 0; i < document.styleSheets.length; ++i) {
            if (document.styleSheets[i] === ss) {
                switch (method) {
                    case "close":
                        ss.disabled = true;
                        break;
                    case "open":
                        ss.disabled = false;
                        break;
                    default:
                        ss.disabled = !ss.disabled;
                        break;
                }

                break;
            }
        }
    }
};//控制样式表的开关

wkh.tools.memorize = function (f, thisObj) {//接收一个函数作为参数，返回带记忆能力的函数，牺牲空间复杂度换取更优的时间复杂度
    var cache = {};//将值保存
    thisObj = thisObj || window;

    return function () {
        var key = arguments.length + Array.prototype.join.call(arguments, ",");//将参数转化为缓存键值
        if (key in cache) return cache[key];
        else return cache[key] = f.apply(thisObj, arguments);
    };
};

wkh.tools.colors = {//命名颜色值，返回rgb的数组
    aliceblue: [240, 248, 255],
    antiquewhite: [250, 235, 215],
    aqua: [0, 255, 255],
    aquamarine: [127, 255, 212],
    azure: [240, 255, 255],
    beige: [245, 245, 220],
    bisque: [255, 228, 196],
    black: [0, 0, 0],
    blanchedalmond: [255, 235, 205],
    blue: [0, 0, 255],
    blueviolet: [138, 43, 226],
    brown: [165, 42, 42],
    burlywood: [222, 184, 135],
    cadetblue: [95, 158, 160],
    chartreuse: [127, 255, 0],
    chocolate: [210, 105, 30],
    coral: [255, 127, 80],
    cornflower: [100, 149, 237],
    cornsilk: [255, 248, 220],
    crimson: [220, 20, 60],
    cyan: [0, 255, 255],
    darkblue: [0, 0, 139],
    darkcyan: [0, 139, 139],
    darkgoldenrod: [184, 134, 11],
    darkgray: [169, 169, 169],
    darkgreen: [0, 100, 0],
    darkkhaki: [189, 183, 107],
    darkmagenta: [139, 0, 139],
    darkolivegreen: [85, 107, 47],
    darkorange: [255, 140, 0],
    darkorchid: [153, 50, 204],
    darkred: [139, 0, 0],
    darksalmon: [233, 150, 122],
    darkseagreen: [143, 188, 139],
    darkslateblue: [72, 61, 139],
    darkslategray: [47, 79.79],
    darkturquoise: [0, 206, 209],
    darkviolet: [148, 0, 211],
    deeppink: [255, 20, 147],
    deepskyblue: [0, 191, 255],
    dimgray: [105, 105, 105],
    dodgerblue: [30, 144, 255],
    firebrick: [178, 34, 34],
    floralwhite: [255, 250, 240],
    forestgreen: [34, 139, 34],
    fuchsia: [255, 0, 255],
    gainsboro: [220, 220, 220],
    ghostwhite: [248, 248, 255],
    gold: [255, 215, 0],
    goldenrod: [218, 165, 32],
    gray: [128, 128, 128],
    green: [0, 128, 0],
    greenyellow: [173, 255, 47],
    honeydew: [240, 255, 240],
    hotpink: [255, 105, 180],
    indianred: [205, 92, 92],
    indigo: [75, 0, 130],
    ivory: [255, 255, 240],
    khaki: [240, 230, 140],
    lavender: [230, 230, 250],
    lavenderblush: [255, 240, 245],
    lawngreen: [124, 252, 0],
    lemonchiffon: [255, 250, 205],
    lightblue: [173, 216, 230],
    lightcoral: [240, 128, 128],
    lightcyan: [224, 255, 255],
    lightgoldenrodyellow: [250, 250, 210],
    lightgreen: [144, 238, 144],
    lightgrey: [211, 211, 211],
    lightpink: [255, 182, 193],
    lightsalmon: [255, 160, 122],
    lightseagreen: [32, 178, 170],
    lightskyblue: [135, 206, 250],
    lightslategray: [119, 136, 153],
    lightsteelblue: [176, 196, 222],
    lightyellow: [255, 255, 224],
    lime: [0, 255, 0],
    limegreen: [50, 205, 50],
    linen: [250, 240, 230],
    megenta: [255, 0, 255],
    maroon: [128, 0, 0],
    mediumaquamarine: [102, 205, 170],
    mediumblue: [0, 0, 205],
    mediumorchid: [186, 85, 211],
    mediumpurple: [147, 112, 219],
    mediumseagreen: [60, 179, 113],
    mediumslateblue: [123, 104, 238],
    mediumspringgreen: [0, 250, 154],
    mediumturquoise: [72, 209, 204],
    mediumvioletred: [199, 21, 133],
    midnightblue: [25, 25, 112],
    mintcream: [245, 255, 250],
    mistyrose: [255, 228, 225],
    moccasin: [255, 228, 181],
    navajowhite: [255, 222, 173],
    navy: [0, 0, 128],
    oldlace: [253, 245, 230],
    olive: [128, 128, 0],
    olivedrab: [107, 142, 35],
    orange: [255, 165, 0],
    orangered: [255, 69, 0],
    orchid: [218, 112, 214],
    palegoldenrod: [238, 232, 170],
    palegreen: [152, 251, 152],
    paleturquoise: [175, 238, 238],
    palevioletred: [219, 112, 147],
    papayawhip: [255, 239, 213],
    peachpuff: [255, 218, 185],
    peru: [205, 133, 63],
    pink: [255, 192, 203],
    plum: [221, 160, 221],
    powderblue: [176, 224, 230],
    purple: [128, 0, 128],
    red: [255, 0, 0],
    rosybrown: [188, 143, 143],
    royalblue: [65, 105, 225],
    saddlebrown: [139, 69, 19],
    salmon: [250, 128, 114],
    sandybrown: [244, 164, 96],
    seagreen: [46, 139, 87],
    seashell: [255, 245, 238],
    sienna: [160, 82, 45],
    silver: [192, 192, 192],
    skyblue: [135, 206, 235],
    slateblue: [106, 90, 205],
    slategray: [112, 128, 114],
    snow: [255, 250, 250],
    springgreen: [0, 255, 127],
    steelblue: [70, 130, 180],
    tan: [210, 180, 140],
    teal: [0, 128, 128],
    thistle: [216, 191, 216],
    tomato: [255, 99, 71],
    turquoise: [64, 224, 208],
    violet: [238, 130, 238],
    wheat: [245, 222, 179],
    white: [255, 255, 255],
    whitesmoke: [245, 245, 245],
    yellow: [255, 255, 0],
    yellowgreen: [154, 205, 50]
};

wkh.tools.analyticColor = function (color) {
    color = color.trim().toLowerCase();
    var result;

    if (color === "transparent") {
        return [0,0,0,0];
    }

    if (result = wkh.tools.colors[color.toLowerCase()]) {
        return result;
    }
    //解析rgb(r,g,b)形式颜色
    if (result = /rgb\((\s*[0-9]{1,3}\s*),(\s*[0-9]{1,3}\s*),(\s*[0-9]{1,3}\s*)\)/i.exec(color)) {
        return [parseInt(result[1],10),
			parseInt(result[2],10),
			parseInt(result[3],10)];
    }
    //解析#RGB形式颜色
    if (result = /#([a-f0-9])([a-f0-9])([a-f0-9])/.exec(color)) {
        return [parseInt(result[1] + result[1], 16),
			parseInt(result[2] + result[2], 16),
			parseInt(result[3] + result[3], 16)];
    }
    //解析rgba(r,g,b,a)形式颜色
    if (result = /rgba\((\s*[0-9]{1,3}\s*),(\s*[0-9]{1,3}\s*),(\s*[0-9]{1,3}\s*),(\s*[0-9\.]+\s*)\)/i.exec(color)) {
        return [parseInt(result[1],10),
			parseInt(result[2],10),
			parseInt(result[3],10),
			parseFloat(result[4]),10];
    }
    //解析#RRGGBB形式颜色
    if (result = /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/.exec(color)) {
        return [parseInt(result[1], 16),
			parseInt(result[2], 16),
			parseInt(result[3], 16)];
    }
    //解析#AARRGGBB形式颜色
    if (result = /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/.exec(color)) {
        return [parseInt(result[2], 16),
			parseInt(result[3], 16),
			parseInt(result[4], 16),
			parseInt(result[1], 16) / 255];
    }
};

wkh.tools.preFixCss = wkh.tools.memorize(function (cssRules) {//如果有，返回带前缀的css名(具有记忆功能)
    if (!cssRules) {
        return cssRules;
    }

    var matched = null;
    if ((matched = /^(-?webkit|-?ms|-?moz|-?o)?(-?)([a-zA-Z0-9]*)/.exec(cssRules)) && cssRules in document.body.style) {
        return cssRules;
    }

    if (matched) {
        var cssRules = matched[3];
        cssRules = cssRules.slice(0, 1).toLowerCase() + cssRules.slice(1);
        if (cssRules in document.body.style) {
            return cssRules;
        }
        if (matched[2] === "-") {
            var temArr = cssRules.split("-");
            for (var i = 1; i < length; ++i) {
                temArr[i] = temArr[i].slice(0, 1).toUpperCase() + temArr[i].slice(1);
            }
            cssRules = temArr.join("");
        }
        
    }

    if (cssRules.length === 1) {//首字母转为大写，保存副本
        var CssRules = cssRules.toUpperCase();
    } else {
        var CssRules = cssRules.slice(0, 1).toUpperCase() + cssRules.slice(1);
    }

    if ("ms" + CssRules in document.body.style) {
        return "ms" + CssRules;
    } else if ("webkit" + CssRules in document.body.style) {
        return "webkit" + CssRules;
    } else if ("moz" + CssRules in document.body.style) {
        return "moz" + CssRules;
    } else if ("o" + CssRules in document.body.style) {
        return "o" + CssRules;
    } else {
        return null;
    }
}, wkh.tools);

wkh.tools.getSameAncestralNode = function (/*...*/) {//找到元素共同祖先元素(如果可能，可包括自身元素)//效率有待调整
    if (arguments.length === 0) {
        throw new Error("没有参数");
    }

    var isArgumentsSame = true;//所有参数是否相同
    for (var i in arguments) {
        if (arguments[i] !== arguments[0]) {
            isArgumentsSame = false;
            break;
        }
    }

    if (isArgumentsSame) {//若相同，反回参数
        return arguments[0];
    }

    var nodeList = [];//每个参数元素的祖先元素列表
    for (var i in arguments) {
        var nowNode = arguments[i];
        var thisNodeList = [];//当前参数祖先元素列表;
        do {
            thisNodeList.unshift(nowNode);
        } while (nowNode = (nowNode.parentNode || nowNode.parentElement));

        nodeList.push(thisNodeList);
    }

    //去除相同的元素
    var sameNode = null;

    while (true) {
        var isSame = true;
        for (var i in nodeList) {
            if (nodeList[i][0] !== nodeList[0][0]) {//如果不相同，跳出
                isSame = false;
                break;
            }
        }

        if (isSame) {//如果相同
            sameNode = nodeList[i][0];
            for (var i in nodeList) {//推出相同元素
                nodeList[i].shift();
            }
        } else {//否则跳出循环
            break;
        }
    }

    return sameNode;
};

wkh.tools.getUrlArgs = function () {//解析url查询参数
    var args = {};
    var query = location.search.substring(1);//去除查询串中的"?"
    var pairs = query.split("&");
    var length = pairs.length;
    for (var i = 0; i < length; ++i) {
        var pos = pairs[i].indexOf('=');
        if (pos === -1) continue;//如果不存在'='号，跳过
        var name = pairs[i].substring(0, pos);
        var value = pairs[i].substring(pos + 1);
        value = decodeURIComponent(value);//对value进行解码

        if (name in args) {//如果存在
            try{
                args[name].push(value);
            } catch (e) {
                args[name] = [args[name], value];
            }
        } else {
            args[name] = value;
        }
    }

    args.length = pairs.length;
    return args;
};

wkh.tools.getRandomNum = function (min, max, isInt) {//产生随机数函数
    min = min || 0;
    max = max || 10;
    isInt = isInt === false ? false : true;

    var range = max - min;
    var rand = Math.random();

    if (isInt) {
        return (min + Math.round(rand * range));
    } else {
        return (min + rand * range);
    }
};

wkh.tools.getRandomNumList = function (length, min, max, isInt) {//产生不重复的随机数列表
    var numArr = [];

    var random;
    while(numArr.length<length) {
        random = wkh.tools.getRandomNum(min, max, isInt);
        if (numArr.contains(random) === -1) {
            numArr.push(random);
        }
    }
    return numArr;
};

wkh.tools.getInputSelection = function (inputDom) {//获得input对象选中文本
    var tagName = inputDom.tagName.toUpperCase();
    if (tagName !== "INPUT" && tagName !== "TEXTAREA") {
        throw new Error("对象不是文本输入框");
    }
    if ("selectionStart" in inputDom) {
        if (document.activeElement === inputDom) {
            return inputDom.value.substring(inputDom.selectionStart, inputDom.selectionEnd);
        } else {
            return "";
        }
    } else {
        var range = document.selection.createRange();
        if (range.parentElement() === inputDom) {
            return range.text;
        } else {
            return "";
        }
    }
};

wkh.tools.setInputSelection = function (inputDom, startIndex, endIndex) {//设置input地象选中文本
    var tagName = inputDom.tagName.toUpperCase();
    if (tagName !== "INPUT" && tagName !== "TEXTAREA") {
        throw "对象不是文本输入框";
    }
    if (inputDom.setSelectionRange) {
        inputDom.setSelectionRange(startIndex, endIndex);
    }
    else if (inputDom.createTextRange) //IE 
    {
        var range = inputDom.createTextRange();
        range.collapse(true);
        range.moveStart('character', startIndex);
        range.moveEnd('character', endIndex - startIndex);
        range.select();
    }
    inputDom.focus();
};

(function () {

    var tempSameName = {};

    wkh.tools.using = function (o) {
        var className;
        if (!(className = o.__wkh_private_className)) {//如果不具有类名属性
            throw new SyntaxError("该对象不是一个合法的类");
        }

        var obj = window[className];
        if (obj != null) {
            if (obj === o) {
                return;
            } else {
                console.log(className+"类：类名与全局就能量冲突，可以使用unUsing或重写js源码回复原值")

                tempSameName[className] = obj;
                window[className] = o;
            }
        } else {
            window[className] = o;
        }
    };

    wkh.tools.using.__wkh_private_className = "using";
    wkh.using = wkh.tools.using;

    wkh.tools.unUsing = function (o) {
        var className;
        if (!(className = o.__wkh_private_className)) {//如果不具有类名属性
            throw new SyntaxError("该对象不是一个合法的类");
        }

        if (tempSameName[className]!=null) {
            window[className] = tempSameName[className];
        } else {
            try{
                delete window[className];
            } catch (e) {
                window[className] = undefined;
            }
        }
    };

    wkh.tools.unUsing.__wkh_private_className = "unUsing";
    wkh.unUsing = wkh.tools.unUsing;
}());

(function () {
    var $;
    if (window.$ != null && window.$ !== wkh.tools) {//
        console.log("$全局变量冲突,可使用fullName方法使用全名并回复原变量");

        $ = window.$;
    }

    window.$ = wkh.tools;
    wkh.select = wkh.tools;


    wkh.tools.fullName = function () {
        if ($ != null) {
            window.$ = $;
        } else {
            try {
                delete window.$;
            } catch (e) {
                window.$ = undefined;
            }
        }
    };
}());

(function () {
  function alertBookmark(isAlert, alertText) {
    if (isAlert === true) {
      if (!alertText) {
        alertText = "该浏览器无此功能，请按 Ctrl + D 手动收藏该页面";
      }

      alert(alertText);
    } else {
      console.log("该浏览器无添加收藏的功能。");
    }
  }
  wkh.tools.addBookmark = function (isAlert, alertText) {
    var title = document.title
    var url = document.location.href
    if (window.sidebar) window.sidebar.addPanel(title, url, "");
    else if (window.opera && window.print) {
      var mbm = document.createElement('a');
      mbm.setAttribute('rel', 'sidebar');
      mbm.setAttribute('href', url);
      mbm.setAttribute('title', title);
      mbm.click();
    }
    else if (document.all) {
      try {
        window.external.AddFavorite(url, title);
      } catch (e) {
        alertBookmark(isAlert, alertText);
      }
    } else {
      alertBookmark(isAlert, alertText);
    }
  };
}());

wkh.tools.loop = function (fn, time) {//定时循环
    if (!time) {
        time = 300;
    }

    function loopfn() {
        if (fn() === false) {
            setTimeout(loopfn, time);
        }
    }

    setTimeout(loopfn, time);
};

wkh.tools.setInnerKey = function (obj,key, val) {//如果可以，设置对象内置方法或值，不可枚举和配置
    try {
        Object.defineProperty(obj, key, {
            writable: true,
            enumerable: false,
            configurable: false,
            value: val
        });
    } catch (e) {
        obj[key] = val;
    }
};

wkh.tools.realBoxSize = function (dom) {
    var body = document.body || document.getElementsByTagName("body")[0];
    var div = document.createElement("div");
    div.css({
        position: "absolute",
        visibility: "hidden",
        padding: 0,
        margin: 0,
        border: "none"
    });
    body.appendChild(div);

    var cloneNode = dom.cloneNode(true);
    div.appendChild(cloneNode);
    var returnBox = { width: cloneNode.css("width"), height: cloneNode.css("height") };

    div.removeNode(true);
    return returnBox;
};

(function () {//动画事件检测
    var pre = ["webkit", "o", "moz"];
    var transitionNames = {
        "webkit": "webkitTransitionEnd",
        "o": "oTransitionEnd"
    };
    var animationNames = {
        "webkit": ["webkitAnimationStart", "webkitAnimationEnd", "webkitAnimationIteration"],
        "o": ["oAnimationStart", "oAnimationEnd", "oAnimationIteration"]
    };

    var thisPre = "", thisTransitionName = "transitionend", thisAnimationName = ["animationstart", "animationend", "animationiteration"];
    var testDivStyle = document.createElement("div").style;

    if ("transition" in testDivStyle && "animation" in testDivStyle) {
    } else {
        for (var i = 0, l = pre.length; i < l; ++i) {
            thisPre = pre[i];
            if ((thisPre + "Transition") in testDivStyle || (thisPre + "Animation") in testDivStyle) {
                break;
            }
            thisPre = false;
        }
        if (thisPre !== false) {
            if (thisPre !== "moz") {
                if (!("transition" in testDivStyle)) {
                    thisTransitionName = transitionNames[thisPre];
                }
                if (!("animation" in testDivStyle)) {
                    thisAnimationName = animationNames[thisPre]
                }
            }
        } else {
            thisTransitionName = "";
            thisAnimationName = ["", "", ""];
        }
    }

    wkh.tools.event = {};
    wkh.tools.event.transitionend = thisTransitionName;
    wkh.tools.event.animationstart = thisAnimationName[0];
    wkh.tools.event.animationend = thisAnimationName[1];
    wkh.tools.event.animationiteration = thisAnimationName[2];
}());